package com.opensource.excel.core;

import java.io.File;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;

public class XmlParser {

	final static String ROOT  = "table";
	final static String ID    = "id";
	final static String CLASS = "class";
	final static String ROW   = "tr";
	final static String CELL  = "td";

	final static String CELL_FIELD    = "field";
	final static String CELL_REQUIRED = "required";
	final static String CELL_TYPE     = "type";

	/**
	 * 解析模板文件， 并形成缓存数据
	 * 
	 * @param file
	 * @throws Exception
	 */
	public static void parse(File file) throws Exception {
		DocumentBuilderFactory factory  = DocumentBuilderFactory.newInstance();
		DocumentBuilder        builder  = factory.newDocumentBuilder();
		Document               document = builder.parse(IoUtil.toStream(file));
		XmlParser.parse(document);
	}

	/**
	 * 解析文档， 并把配置文档中的信息缓存下来
	 * 
	 * @param document
	 * @throws Exception
	 */
	public static void parse(Document document) throws Exception {
		NodeList roots = document.getElementsByTagName(ROOT);
		if (null != roots) {
			for (int index = 0; index < roots.getLength(); index++) {
				XTable xTable = parse(roots, index);
				XReader.CACHE.put(xTable.getId(), xTable);
			}
		}
	}

	/**
	 * 清除解析配置文档的缓存数据
	 */
	public static void clearCache() {
		XReader.CACHE.clear();
	}

	/**
	 * 解析表主键
	 * 
	 * @param roots
	 * @param index
	 * @return
	 */
	private static XTable parse(NodeList roots, int index) throws Exception {
		Node         tableNode    = roots.item(index);
		NamedNodeMap namedNodeMap = tableNode.getAttributes();
		Node         idNode       = namedNodeMap.getNamedItem(ID);
		if (null == idNode) {
			throw new RuntimeException("模板表主键不存在");
		}
		String   id        = idNode.getNodeValue();
		Node     clazzNode = namedNodeMap.getNamedItem(CLASS);
		Class<?> clazz     = null;
		if (null == clazzNode) {
			clazz = HashMap.class;
		} else {
			clazz = Class.forName(clazzNode.getNodeValue());
		}
		XTable xTable = new XTable();
		xTable.setId(id);
		xTable.setClazz(clazz);

		if (!tableNode.hasChildNodes()) {
			throw new RuntimeException("tr标签使用不正确");
		}
		NodeList rowsNode = tableNode.getChildNodes();
		for (int rowIndex = 0; rowIndex < rowsNode.getLength(); rowIndex++) {
			XRow xRow = parseRow(rowsNode, rowIndex);
			if (null != xRow) {
				xTable.addRow(xRow);
			}
		}
		if (CollUtil.isEmpty(xTable.getRows())) {
			throw new RuntimeException("行数据不能为空行");
		}
		return xTable;
	}

	/**
	 * 解析行数据
	 * 
	 * @param rowsNode
	 * @param rowIndex
	 * @return
	 */
	private static XRow parseRow(NodeList rowsNode, int index) {
		Node rowNode = rowsNode.item(index);
		XRow xRow    = null;
		if (StrUtil.equalsIgnoreCase(ROW, rowNode.getNodeName())) {
			xRow = new XRow();
			if (!rowNode.hasChildNodes()) {
				throw new RuntimeException("单元格不能是单标签单元格");
			}
			NodeList cellsNode = rowNode.getChildNodes();
			for (int cellIndex = 0; cellIndex < cellsNode.getLength(); cellIndex++) {
				XCell xCell = parseCell(cellsNode, cellIndex);
				if (null != xCell) {
					xRow.addCell(xCell);
				}
			}
		}
		return xRow;
	}

	/**
	 * 解析列的数据列表信息
	 * 
	 * @param cellsNode
	 * @param index
	 * @return
	 */
	private static XCell parseCell(NodeList cellsNode, int index) {
		Node  cellNode = cellsNode.item(index);
		XCell xCell    = null;
		if (StrUtil.equalsIgnoreCase(CELL, cellNode.getNodeName())) {
			NamedNodeMap namedNodeMap = cellNode.getAttributes();
			String       field        = getCellAttr(namedNodeMap, CELL_FIELD);
			String       type         = getCellAttr(namedNodeMap, CELL_TYPE);
			if (StrUtil.isNotBlank(field)) {
				String required = getCellAttr(namedNodeMap, CELL_REQUIRED);
				xCell = new XCell();
				xCell.setField(field);
				xCell.setRequired(BooleanUtil.toBoolean(required));
				// 与excel端保持统一的去特殊字符的功能
				xCell.setText(StrKit.delSpecialChar(cellNode.getTextContent().trim()));
				setXCellType(xCell, type);
			}
		}
		return xCell;
	}

	/**
	 * 设置数据类型， 支持 string/int/boolean/decimal/long类型
	 * 
	 * @param xCell
	 * @param type
	 */
	private static void setXCellType(XCell xCell, String type) {
		type = StrUtil.nullToDefault(type, "string");
		switch (type) {
		case "string":
			xCell.setType(String.class);
			break;
		case "long":
			xCell.setType(Long.class);
			break;
		case "boolean":
			xCell.setType(Boolean.class);
			break;
		case "decimal":
			xCell.setType(BigDecimal.class);
			break;
		case "int":
			xCell.setType(Integer.class);
			break;
		case "date":
			xCell.setType(Date.class);
			break;
		case "double":
			xCell.setType(Double.class);
			break;
		default:
			break;
		}
	}

	/**
	 * 获取属性信息
	 * 
	 * @param namedNodeMap
	 * @param itemName
	 * @return
	 */
	private static String getCellAttr(NamedNodeMap namedNodeMap, String itemName) {
		Node attrNode = namedNodeMap.getNamedItem(itemName);
		if (null != attrNode) {
			String value = attrNode.getNodeValue();
			return value.trim();
		}
		return null;
	}

}
